import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.swing.JTable;

import org.junit.*;
import static org.junit.Assert.*;

import evaluators.*;
import resources.*;

public class JUnitTests 
{
	Frame frame;
	JTable table;
	
	@Before
	public void setUp()
	{
		frame = new Frame();
		Frame.tm2.initiateValues();
		table = new JTable(Frame.tm2);
	} // setup the spreadsheet
	
	@Test
	public void test1()
	{
		try {
			IntegerEval test1_1 = new IntegerEval();
			Tools.addVariable("A0:7");
			assertTrue(test1_1.evaluate("A0") == 7);
			assertTrue(test1_1.evaluate("(((A0)))") == 7);		
		} 
		catch (VariableAssignmentException e) {} 
		catch (NoSuchVariableExistsException e) {} 
		catch (InvalidExpressionException e) {}
		
		Frame.tm2.setValueAt(7,0,0); //A0
		assertTrue(table.getValueAt(0,0).equals("7"));
	} // assignment op
	
	@Test
	public void test2()
	{
		try{
			IntegerEval test2_1 = new IntegerEval();
			Tools.addVariable("A0:7");
			Tools.addVariable("A1:2");
			assertTrue(Tools.variableExists("A0"));
			assertTrue(Tools.variableExists("A1"));
			assertTrue(test2_1.evaluate("A0+A1") == 9);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	} //addition
	
	@Test
	public void test3()
	{
		try{
			IntegerEval test2_1 = new IntegerEval();
			Tools.addVariable("A0:7");
			Tools.addVariable("A1:2");
			assertTrue(Tools.variableExists("A0"));
			assertTrue(Tools.variableExists("A1"));
			assertTrue(test2_1.evaluate("A0-A1") == 5);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	} //subtraction
	
	@Test
	public void test4()
	{
		try{
			IntegerEval test2_1 = new IntegerEval();
			Tools.addVariable("A0:7");
			Tools.addVariable("A1:2");
			assertTrue(Tools.variableExists("A0"));
			assertTrue(Tools.variableExists("A1"));
			assertTrue(test2_1.evaluate("A0*A1") == 14);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	} //multiplication
	
	@Test
	public void test5()
	{
		try{
			IntegerEval test2_1 = new IntegerEval();
			Tools.addVariable("A0:10");
			Tools.addVariable("A1:2");
			assertTrue(Tools.variableExists("A0"));
			assertTrue(Tools.variableExists("A1"));
			assertTrue(test2_1.evaluate("A0/A1") == 5);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	} // division
	
	@Test
	public void test6()
	{
		try{
			IntegerEval test2_1 = new IntegerEval();
			Tools.addVariable("A0:7");
			Tools.addVariable("A1:A0");
			assertTrue(Tools.variableExists("A0"));
			assertTrue(Tools.variableExists("A1"));
			assertTrue(test2_1.evaluate("A1") == 7);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	} //assigning new var to old var
	
	@Test
	public void test7()
	{
		try{
			IntegerEval test2_1 = new IntegerEval();
			Tools.addVariable("A0:7");
			Tools.addVariable("A1:2");
			assertTrue(Tools.variableExists("A0"));
			assertTrue(Tools.variableExists("A1"));
			assertTrue(test2_1.evaluate("A0^A1") == 49);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	} // exponent
	
	@Test
	public void test8()
	{
		try{
			IntegerEval test2_1 = new IntegerEval();
			Tools.addVariable("A0:10");
			Tools.addVariable("A1:2");
			assertTrue(Tools.variableExists("A0"));
			assertTrue(Tools.variableExists("A1"));
			assertTrue(test2_1.evaluate("A0%A1") == 0);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	} // modulo
	
	@Test
	public void test9()
	{
		try{
			BooleanEval test2_1 = new BooleanEval();
			Tools.addVariable("A0:true");
			Tools.addVariable("A1:!false");
			assertTrue(Tools.variableExists("A0"));
			assertTrue(Tools.variableExists("A1"));
			assertTrue(test2_1.evaluate("A0 & A1") == true);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	} // boolean
	
	@Test
	public void test10()
	{
		try{
			IntegerEval test2_1 = new IntegerEval();
			Tools.addVariable("A1:8");
			Tools.addVariable("A2:3");
			assertTrue(Tools.variableExists("A1"));
			assertTrue(Tools.variableExists("A2"));
			assertTrue(test2_1.evaluate("A1*(A1-A2)+A2/3") == 41);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	} // #3 on handout
	
	@Test
	public void test11()
	{
		try{
			IntegerEval test2_1 = new IntegerEval();
			Tools.addVariable("A1:10");
			Tools.addVariable("B1:7");
			assertTrue(Tools.variableExists("A1"));
			assertTrue(Tools.variableExists("B1"));
			Tools.addVariable("A2:A1+B1");
			assertTrue(test2_1.evaluate("A2") == 17);
			Tools.addVariable("B2:A2");
			assertTrue(test2_1.evaluate("B2") == 17);
			Tools.addVariable("A3:A2+B2");
			assertTrue(test2_1.evaluate("A3") == 34);
			Tools.addVariable("B3:A3-A2");
			assertTrue(test2_1.evaluate("B3") == 17);
			Tools.addVariable("A4:A3");
			assertTrue(test2_1.evaluate("A4") == 34);
			Tools.addVariable("B4:A4+B3");
			assertTrue(test2_1.evaluate("B4") == 51);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	} // #4 on handout
	
	@Test
	public void test12()
	{
		try{
			IntegerEval test2_1 = new IntegerEval();
			Tools.addVariable("A0:7");
			Tools.addVariable("A1:2");
			assertTrue(Tools.variableExists("A0"));
			assertTrue(Tools.variableExists("A1"));
			Tools.addVariable("A0:A1");
			assertTrue(test2_1.evaluate("A0") == 2);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	}
	
	@Test (expected=InvalidExpressionException.class)
	public void test13() throws NoSuchVariableExistsException, InvalidExpressionException, VariableAssignmentException
	{
			IntegerEval test2_1 = new IntegerEval();
			Tools.addVariable("A0:7");
			test2_1.evaluate("((A0))))");
	} // invalid expression
	
	@Test
	public void test14()
	{
		try{
			BooleanEval test2_1 = new BooleanEval();
			Tools.addVariable("A0:17");
			Tools.addVariable("A1:5");
			Tools.addVariable("A2:5");
			assertTrue(Tools.variableExists("A0"));
			assertTrue(Tools.variableExists("A1"));
			assertTrue(test2_1.evaluate("A0 > A1") == true);
			assertTrue(test2_1.evaluate("A0 < A1") == false);
			assertTrue(test2_1.evaluate("A1 <= A2") == true);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	} // < and > op
	
	@Test
	public void test15()
	{
		try{
			BooleanEval test2_1 = new BooleanEval();
			Tools.addVariable("A0:147");
			Tools.addVariable("A1:146");
			Tools.addVariable("A2:146");
			assertTrue(Tools.variableExists("A0"));
			assertTrue(Tools.variableExists("A1"));
			assertTrue(test2_1.evaluate("A0 != A1") == true);
			assertTrue(test2_1.evaluate("A1 = A2") == true);
		}
		catch(VariableAssignmentException e){}
		catch(NoSuchVariableExistsException e){} 
		catch(InvalidExpressionException e) {}
	} // = and != op
	
	@Test
	public void test16()
	{
		try {
			IntegerEval test1_1 = new IntegerEval();
			Tools.addVariable("A0:~7");
			assertTrue(test1_1.evaluate("A0") == (-7));
			assertTrue(test1_1.evaluate("(((A0)))") == (-7));		
		} 
		catch (VariableAssignmentException e) {} 
		catch (NoSuchVariableExistsException e) {} 
		catch (InvalidExpressionException e) {}
	} // negative numbers
	
	@Test
	public void test17()
	{
		try{
			DoubleEval test2_1 = new DoubleEval();
			Tools.addVariable("A0:49.0");
			Tools.addVariable("A1:7.0");
			assertTrue(test2_1.evaluate("A0/A1") == 7.0);
		} 
		catch (VariableAssignmentException e) {} 
		catch (NoSuchVariableExistsException e) {} 
		catch (InvalidExpressionException e) {}
	} // doubles
	
	@Test
	public void test18()
	{
		try{
			FloatEval test2_1 = new FloatEval();
			Tools.addVariable("A0:49.0f");
			Tools.addVariable("A1:7.0f");
			assertTrue(test2_1.evaluate("A0/A1") == (7.0f));
		}
		catch (VariableAssignmentException e) {} 
		catch (NoSuchVariableExistsException e) {} 
		catch (InvalidExpressionException e) {}
	} // floats
	@Test
	public void test19() throws java.lang.NullPointerException
	{
		table.setValueAt(23, 0, 10);
		Frame.cut(0,10);
		assertTrue(table.getValueAt(0,10).equals(""));
	}
	
	@Test
	public void test20()
	{
		table.setValueAt(8, 0, 0);
		Frame.copy(0,0);
		assertTrue(table.getValueAt(0,0).equals("8"));
		Frame.paste(1,1);
		assertTrue(table.getValueAt(1,1).equals("8"));
	}
	
	@Test
	public void test21() throws java.lang.StackOverflowError 
	{
		try
		{
			IntegerEval test = new IntegerEval();
			Tools.addVariable("A0:7");
			Frame.tm2.setValueAt(7,0,0);
			frame.updateTables();
			assertTrue(test.evaluate("A0").toString().equals((frame.tm1.getValueAt(0,0))));
		} // may need .equals instead
		catch (VariableAssignmentException e) {
		}
		catch (NoSuchVariableExistsException e) {
		}
		catch (InvalidExpressionException e) {
		}
	}

	@Test
	public void test22()  throws VariableAssignmentException
	{
			Tools.addVariable("A0:D2"); // D2 is nothing right now
			Frame.tm2.setValueAt("D2", 0, 0); //set bad variable in A0

	}

	@Test
	public void test23() throws InvalidExpressionException
	{
		try
		{
			IntegerEval test = new IntegerEval();
			Frame.tm2.setValueAt("A2::C2",0,0); //set bad exp in A0
			assertTrue(test.evaluate("A0").equals("#BAD-EXPRESSION!"));
		}
		catch (VariableAssignmentException e) {
		}
		catch (NoSuchVariableExistsException e) {
		}
		catch (InvalidExpressionException e) {
		}
	}
	
	@Test
	public void test24()
	{
		System.out.println("Test - 24: Testing Simple Integer Cell");
		Frame.tm2.setValueAt("5",0,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("5"));
	}
	
	@Test
	public void test25()
	{
		System.out.println("Test - 25: Testing Simple Double Cell");
		Frame.tm2.setValueAt("5.5",0,0);
		assertTrue(Math.abs(Double.parseDouble(frame.tm1.getValueAt(0, 0).toString())- 5.5) < .0001 );
	}
	
	@Test
	public void test26()
	{
		System.out.println("Test - 26: Testing Simple Boolean Cell");
		Frame.tm2.setValueAt("true",0,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("true"));
	}
	
	@Test
	public void test27()
	{
		System.out.println("Test - 27: Testing Simple String Cell");
		Frame.tm2.setValueAt("\"foo\"",0,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("\"foo\""));
	}
	
	@Test
	public void test28()
	{
		System.out.println("Test - 28: Testing Simple Integer Expression");
		Frame.tm2.setValueAt("2 + 4 * 10 / (2 - 1)",0,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("42"));
	}
	
	@Test
	public void test29()
	{
		System.out.println("Test - 29: Testing Simple Double Expression");
		Frame.tm2.setValueAt("2.5 + 4.0 * 10.0 / (4.0 - 1.0)",0,0);
		assertTrue(Math.abs(Double.parseDouble(frame.tm1.getValueAt(0, 0).toString())- 15.8333333) < .0001 );
	}
	
	@Test
	public void test30()
	{
		System.out.println("Test - 30: Testing Simple Boolean Expression");
		Frame.tm2.setValueAt("true & (false | false)",0,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("false"));
	}

	@Test
	public void test31()
	{
		System.out.println("Test - 31: Testing Simple String Expression");
		Frame.tm2.setValueAt("\"foo\" + \"bar\"",0,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("\"foobar\""));
	}
	
	@Test
	public void test32()
	{
		System.out.println("Test - 32: Testing Integer Expression Involving Cells");
		Frame.tm2.setValueAt("5",0,0);
		Frame.tm2.setValueAt("6/3",1,0);
		Frame.tm2.setValueAt("A1",2,0);
		Frame.tm2.setValueAt("A2 + A0 * 9",0,1);
		assertTrue(frame.tm1.getValueAt(0, 1).equals("47"));
	}
	
	@Test
	public void test33()
	{
		System.out.println("Test - 33: Testing Double Expression Involving Cells");
		Frame.tm2.setValueAt("5.5",0,0);
		Frame.tm2.setValueAt("A0 * 2.5 - 1",1,0);
		Frame.tm2.setValueAt("A1",2,0);
		Frame.tm2.setValueAt("A2",3,0);
		assertTrue(Math.abs(Double.parseDouble(frame.tm1.getValueAt(3, 0).toString())- 12.75) < .0001 );
	}
	
	@Test
	public void test34()
	{
		System.out.println("Test - 34: Testing Boolean Expression Involving Cells");
		Frame.tm2.setValueAt("true",0,0);
		Frame.tm2.setValueAt("!(A0 | false)",1,0);
		Frame.tm2.setValueAt("A1",2,0);
		Frame.tm2.setValueAt("!A1",3,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("true"));
	}
	
	@Test
	public void test35()
	{
		System.out.println("Test - 35: Testing String Expression Involving Cells");
		Frame.tm2.setValueAt("\"man\"",0,0);
		Frame.tm2.setValueAt("A0 + \"bear\"",1,0);
		Frame.tm2.setValueAt("A1 + \"pig\"",2,0);
		assertTrue(frame.tm1.getValueAt(2, 0).equals("\"manbearpig\""));
	}
	
	@Test
	public void test36() throws VariableAssignmentException
	{
		System.out.println("Test - 36: Testing Integer Expression Using Named Cells");
		Tools.addVariable("foo" + ": " + "A1");
		Tools.addVariable("bar" + ": " + "A2");
		Frame.tm2.setValueAt("99",0,0);
		Frame.tm2.setValueAt("A0 + 1",1,0);
		Frame.tm2.setValueAt("foo + 50",2,0);
		Frame.tm2.setValueAt("bar",3,0);
		assertTrue(frame.tm1.getValueAt(3, 0).equals("150"));
	}
	
	@Test
	public void test37() throws VariableAssignmentException
	{
		System.out.println("Test - 37: Testing Double Expression Using Named Cells");
		Tools.addVariable("foo" + ": " + "A1");
		Tools.addVariable("bar" + ": " + "A2");
		Frame.tm2.setValueAt("99.9",0,0);
		Frame.tm2.setValueAt("A0 + 1.5",1,0);
		Frame.tm2.setValueAt("foo + 50.4",2,0);
		Frame.tm2.setValueAt("bar",3,0);
		assertTrue(Math.abs(Double.parseDouble(frame.tm1.getValueAt(3, 0).toString())- 151.8) < .0001 );
	}
	
	@Test
	public void test38() throws VariableAssignmentException
	{
		System.out.println("Test - 38: Testing Boolean Expression Using Named Cells");
		Tools.addVariable("foo" + ": " + "A1");
		Tools.addVariable("bar" + ": " + "A2");
		Frame.tm2.setValueAt("true",0,0);
		Frame.tm2.setValueAt("A0 & false",1,0);
		Frame.tm2.setValueAt("foo & !false",2,0);
		Frame.tm2.setValueAt("bar",3,0);
		assertTrue(frame.tm1.getValueAt(3, 0).equals("false"));
	}
	
	@Test
	public void test39() throws VariableAssignmentException
	{
		System.out.println("Test - 39: Testing String Expression Using Named Cells");
		Tools.addVariable("foo" + ": " + "A1");
		Tools.addVariable("bar" + ": " + "A2");
		Frame.tm2.setValueAt("\"CS\"",0,0);
		Frame.tm2.setValueAt("A0 + \"3\"",1,0);
		Frame.tm2.setValueAt("foo + \"42\"",2,0);
		Frame.tm2.setValueAt("bar",3,0);
		assertTrue(frame.tm1.getValueAt(3, 0).equals("\"CS342\""));
	}
	
	@Test
	public void test40()
	{
		System.out.println("Test - 40: Testing Invalid Expression (int + boolean)");

		Frame.tm2.setValueAt("5 + true",0,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("#BAD-EXPRESSION!"));
	}
	
	@Test
	public void test41()
	{
		System.out.println("Test - 41: Testing Invalid Expression (too many operators)");

		Frame.tm2.setValueAt("5 + 1 * - 9 ",0,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("#BAD-EXPRESSION!"));
	}
	
	@Test
	public void test42()
	{
		System.out.println("Test - 42: Testing Invalid Expression (too many right parenthesis)");

		Frame.tm2.setValueAt("(5 + 9) * 7)",0,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("#BAD-EXPRESSION!"));
	}
	
	@Test
	public void test43()
	{
		System.out.println("Test - 43: Testing Invalid Expression (too many left parenthesis)");

		Frame.tm2.setValueAt("(((5 + 9) * 7)",0,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("#BAD-EXPRESSION!"));
	}
	
	@Test
	public void test44()
	{
		System.out.println("Test - 44: Testing Invalid Expression (obvious circular reference)");

		Frame.tm2.setValueAt("A0",0,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("#REF-ERROR!"));
	}
	
	@Test
	public void test45() throws VariableAssignmentException
	{
		System.out.println("Test - 45: Testing Invalid Expression (more complex circular reference)");
		Tools.addVariable("foo" + ": " + "A1");
		Frame.tm2.setValueAt("A2 + A1",0,0);
		Frame.tm2.setValueAt("5 + A0",1,0);
		Frame.tm2.setValueAt("foo",2,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("#REF-ERROR!"));
	}
	
	@Test
	public void test46()
	{
		System.out.println("Test - 46: Testing Invalid Expression (using unassigned name)");
		Frame.tm2.setValueAt("bat",2,0);
		assertTrue(frame.tm1.getValueAt(2, 0).equals("#BAD-EXPRESSION!"));
	}
	
	@Test
	public void test47()
	{
		System.out.println("Test - 47: Testing Invalid Expression (int + empty cell)");
		Frame.tm2.setValueAt("1 + C0",0,0);
		assertTrue(frame.tm1.getValueAt(0, 0).equals("#BAD-VAR!"));
	}
	
	@Test
	public void test48() throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, IOException
	{
		System.out.println("Test - 48: Testing Save to XML");
		Frame.tm2.setValueAt("6",0,0);
		frame.saveXML("test.xml");
		frame.clearEverything();
		XMLToSpread.load("test.xml");
		Compress.deleteFile("test.xml");
		assertTrue(frame.tm1.getValueAt(0, 0).equals("6"));
	}
	

}
